﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using CADs;
using System.ComponentModel;
using SeguridadGaleria;
using AvalonDock;
using ModeloGaleriaNET;

namespace GaleriaNET
{
    delegate void AuditoriaThread();
    /// <summary>
    /// Lógica de interacción para ucUserRoles.xaml
    /// </summary>
    public partial class ucUserRoles : UserControl
    {
        private string[] permisosBuscar = new string[] { TiposRoles.DAR_PRIVILEGIOS, TiposRoles.QUITAR_PRIVILEGIO, TiposRoles.CAMBIAR_GRUPO };
        private readonly string title;
        private bool loaded;
        private BackgroundWorker worker, workerChangeUsuario, workerChangeGrupo, workerCambios, workerPermisos;
        private List<string> permisos;
        private ManejadorRoles roleManager;
        private string usuario;
        private List<string> grupos;
        private bool success;
        private bool chargingGroup;
        private string grupo;

        private GrupoValue grupoUsuario;
        private GrupoValue originalGrupo;

        private string[] permisosMarcados;
        private string grupoSeleccionado;
        private string userSeleccionado;
        private bool cambiosHechos;
        private Dictionary<string, bool> permisosDatos;

        private IAuditoria auditoria;

        public ucUserRoles()
        {
            InitializeComponent();
            title = "Roles de usuario";
            loaded = false;
            worker = new BackgroundWorker();
            worker.DoWork += worker_DoWork;
            worker.RunWorkerCompleted += worker_RunWorkerCompleted;
            workerChangeUsuario = new BackgroundWorker();
            workerChangeUsuario.DoWork += workerChangeUsuario_DoWork;
            workerChangeUsuario.RunWorkerCompleted += workerChangeUsuario_RunWorkerCompleted;
            workerChangeGrupo = new BackgroundWorker();
            workerChangeGrupo.DoWork += workerChangeGrupo_DoWork;
            workerChangeGrupo.RunWorkerCompleted += workerChangeGrupo_RunWorkerCompleted;
            workerCambios = new BackgroundWorker();
            workerCambios.DoWork += workerCambios_DoWork;
            workerCambios.RunWorkerCompleted += workerCambios_RunWorkerCompleted;
            workerPermisos = new BackgroundWorker();
            workerPermisos.DoWork += workerPermisos_DoWork;
            workerPermisos.RunWorkerCompleted += workerPermisos_RunWorkerCompleted;
            roleManager = ManejadorRoles.Instance;
            success = false;
            chargingGroup = false;
            auditoria = new AuditoriaBD();
        }

        public string Title
        {
            get { return title; }
        }

        private void cbUsuarios_GotFocus(object sender, RoutedEventArgs e)
        {
            if (cbUsuarios.ItemsSource == null)
            {
                cbUsuarios.ItemsSource = new UsuarioCAD().GetUsuariosIdNombre();
                cbUsuarios.DisplayMemberPath = "PseudonimoLocal";
                cbUsuarios.SelectedValuePath = "PseudonimoLocal";
                cbUsuarios.SelectedIndex = -1;
            }
        }

        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            if (!loaded)
            {
                if (!worker.IsBusy)
                {
                    _busyIndicator.IsBusy = true;
                    workerPermisos.RunWorkerAsync();
                    System.Threading.Thread.Sleep(1000);
                    worker.RunWorkerAsync();
                }
                loaded = true;
            }
        }

        private void workerPermisos_DoWork(object sender, DoWorkEventArgs e)
        {
            string usuario;
            bool? value;

            permisosDatos = new Dictionary<string, bool>();
            usuario = Convert.ToString(Utilities.FindUsuarioData(typeof(UserWindow), this, "Name"));
            foreach (string s in permisosBuscar)
            {
                value = roleManager.CanUse(s, usuario);
                if (value != null)
                    permisosDatos.Add(s, value.Value);
            }
        }

        private void workerPermisos_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            bool value, value2, value3;

            if (!(permisosDatos != null && permisosDatos.Count == permisosBuscar.Length))
            {
                Xceed.Wpf.Toolkit.MessageBox.Show("No se han podido obtener los permisos", "Error",
                    MessageBoxButton.OK, MessageBoxImage.Error);
                cbUsuarios.IsEnabled = cbGrupos.IsEnabled = clbPermisos.IsEnabled = btAceptar.IsEnabled = false;
            }
            else
            {
                permisosDatos.TryGetValue(TiposRoles.CAMBIAR_GRUPO, out value);
                permisosDatos.TryGetValue(TiposRoles.DAR_PRIVILEGIOS, out value2);
                permisosDatos.TryGetValue(TiposRoles.QUITAR_PRIVILEGIO, out value3);

                if (!value && !value2 && !value3)
                {
                    cbUsuarios.IsEnabled = cbGrupos.IsEnabled = clbPermisos.IsEnabled = btAceptar.IsEnabled = false;
                }
                else
                {
                    if (!value)
                        cbGrupos.IsEnabled = false;

                    if (!(value && value2))
                        clbPermisos.IsEnabled = false;
                }
            }
        }

        private void workerCambios_DoWork(object sender, DoWorkEventArgs e)
        {
            string[] permisosNuevoGrupo;
            string[] permisosAdd, permisosDelete;
            List<string> aux;
            int uId = -1;

            Dispatcher.Invoke((AuditoriaThread)delegate()
            {
                uId = Convert.ToInt32(Utilities.FindUsuarioData(typeof(UserWindow), this, "Id"));
            });

            if (originalGrupo.Nombre.Equals(grupoSeleccionado) == false)
            {
                auditoria.Mensaje(new object[] { userSeleccionado, grupoSeleccionado },
                    TiposAuditoria.Ordinal(TiposAuditoria.TIPO_AUDITORIA.INICIO_CAMBIAR_USUARIO_DE_GRUPO), uId);
                // Cambiar grupo
                cambiosHechos = roleManager.CambiarGrupo(userSeleccionado, grupoSeleccionado);
                // Hallar la diferencia de los permisos y añadir/quitar
                permisosNuevoGrupo = roleManager.GetGrupo(grupoSeleccionado).Permisos;

                permisosAdd = permisosMarcados.Except(permisosNuevoGrupo).ToArray();
                aux = permisosAdd.ToList();

                if (grupoUsuario.PermisosNuevos != null)
                    aux.AddRange(grupoUsuario.PermisosNuevos);
                permisosAdd = aux.ToArray();

                if (permisosAdd != null && permisosAdd.Length > 0)
                {
                    auditoria.Mensaje(new object[] { Utilities.ArrayToString(permisosAdd, ", "), userSeleccionado },
                    TiposAuditoria.Ordinal(TiposAuditoria.TIPO_AUDITORIA.INICIO_DAR_PRIVILEGIOS_USUARIO), uId);
                }

                permisosDelete = permisosNuevoGrupo.Except(permisosMarcados).ToArray();
                aux = permisosDelete.ToList();

                if (grupoUsuario.PermisosBorrados != null)
                {
                    foreach (string s in grupoUsuario.PermisosBorrados)
                        aux.Remove(s);
                }
                permisosDelete = aux.ToArray();

                if (permisosDelete != null && permisosDelete.Length > 0)
                {
                    auditoria.Mensaje(new object[] { Utilities.ArrayToString(permisosDelete, ", "), userSeleccionado },
                    TiposAuditoria.Ordinal(TiposAuditoria.TIPO_AUDITORIA.INICIO_QUITAR_PRIVILEGIOS_USUARIO), uId);
                }

                cambiosHechos = cambiosHechos && roleManager.AddPermisosUsuario(permisosAdd, userSeleccionado);
                cambiosHechos = cambiosHechos && roleManager.DeletePermisosUsuario(permisosDelete, userSeleccionado);

                if (cambiosHechos)
                {
                    auditoria.Mensaje(new object[] { userSeleccionado, grupoSeleccionado },
                    TiposAuditoria.Ordinal(TiposAuditoria.TIPO_AUDITORIA.CAMBIAR_USUARIO_GRUPO), uId);

                    if (permisosAdd != null && permisosAdd.Length > 0)
                    {
                        auditoria.Mensaje(new object[] { Utilities.ArrayToString(permisosAdd, ", "), userSeleccionado },
                        TiposAuditoria.Ordinal(TiposAuditoria.TIPO_AUDITORIA.DAR_PRIVILEGIOS_USUARIO), uId);
                    }

                    if (permisosDelete != null && permisosDelete.Length > 0)
                    {
                        auditoria.Mensaje(new object[] { Utilities.ArrayToString(permisosDelete, ", "), userSeleccionado },
                        TiposAuditoria.Ordinal(TiposAuditoria.TIPO_AUDITORIA.QUITAR_PRIVILEGIOS_USUARIO), uId);
                    }
                }
                else
                {
                    auditoria.Mensaje(new object[] { userSeleccionado, grupoSeleccionado },
                    TiposAuditoria.Ordinal(TiposAuditoria.TIPO_AUDITORIA.ERROR_CAMBIAR_USUARIO_DE_GRUPO), uId);

                    if (permisosAdd != null && permisosAdd.Length > 0)
                    {
                        auditoria.Mensaje(new object[] { Utilities.ArrayToString(permisosAdd, ", "), userSeleccionado },
                        TiposAuditoria.Ordinal(TiposAuditoria.TIPO_AUDITORIA.ERROR_DAR_PRIVILEGIOS_USUARIO), uId);
                    }

                    if (permisosDelete != null && permisosDelete.Length > 0)
                    {
                        auditoria.Mensaje(new object[] { Utilities.ArrayToString(permisosDelete, ", "), userSeleccionado },
                        TiposAuditoria.Ordinal(TiposAuditoria.TIPO_AUDITORIA.ERROR_QUITAR_PRIVILEGIOS_USUARIO), uId);
                    }
                }
            }
            else
            {
                // Hallar la diferencia de los permisos y añadir/quitar
                permisosAdd = permisosMarcados.Except(originalGrupo.Permisos).ToArray();
                permisosDelete = originalGrupo.Permisos.Except(permisosMarcados).ToArray();
                if (permisosAdd != null && permisosAdd.Length > 0)
                {
                    auditoria.Mensaje(new object[] { Utilities.ArrayToString(permisosAdd, ", "), userSeleccionado },
                    TiposAuditoria.Ordinal(TiposAuditoria.TIPO_AUDITORIA.INICIO_DAR_PRIVILEGIOS_USUARIO), uId);
                }

                if (permisosDelete != null && permisosDelete.Length > 0)
                {
                    auditoria.Mensaje(new object[] { Utilities.ArrayToString(permisosDelete, ", "), userSeleccionado },
                    TiposAuditoria.Ordinal(TiposAuditoria.TIPO_AUDITORIA.INICIO_QUITAR_PRIVILEGIOS_USUARIO), uId);
                }

                cambiosHechos = roleManager.AddPermisosUsuario(permisosAdd, userSeleccionado);
                cambiosHechos = cambiosHechos && roleManager.DeletePermisosUsuario(permisosDelete, userSeleccionado);

                if (cambiosHechos)
                {
                    if (permisosAdd != null && permisosAdd.Length > 0)
                    {
                        auditoria.Mensaje(new object[] { Utilities.ArrayToString(permisosAdd, ", "), userSeleccionado },
                        TiposAuditoria.Ordinal(TiposAuditoria.TIPO_AUDITORIA.DAR_PRIVILEGIOS_USUARIO), uId);
                    }

                    if (permisosDelete != null && permisosDelete.Length > 0)
                    {
                        auditoria.Mensaje(new object[] { Utilities.ArrayToString(permisosDelete, ", "), userSeleccionado },
                        TiposAuditoria.Ordinal(TiposAuditoria.TIPO_AUDITORIA.QUITAR_PRIVILEGIOS_USUARIO), uId);
                    }
                }
                else
                {
                    if (permisosAdd != null && permisosAdd.Length > 0)
                    {
                        auditoria.Mensaje(new object[] { Utilities.ArrayToString(permisosAdd, ", "), userSeleccionado },
                        TiposAuditoria.Ordinal(TiposAuditoria.TIPO_AUDITORIA.ERROR_DAR_PRIVILEGIOS_USUARIO), uId);
                    }

                    if (permisosDelete != null && permisosDelete.Length > 0)
                    {
                        auditoria.Mensaje(new object[] { Utilities.ArrayToString(permisosDelete, ", "), userSeleccionado },
                        TiposAuditoria.Ordinal(TiposAuditoria.TIPO_AUDITORIA.ERROR_QUITAR_PRIVILEGIOS_USUARIO), uId);
                    }
                }
            }  
        }

        private void workerCambios_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (cambiosHechos)
            {
                Xceed.Wpf.Toolkit.MessageBox.Show("Cambios realizados con éxito", "Cambios realizados",
                    MessageBoxButton.OK, MessageBoxImage.Information);
            }
            else
            {
                Xceed.Wpf.Toolkit.MessageBox.Show("Falló la modificación de los permisos", "Error", 
                    MessageBoxButton.OK, MessageBoxImage.Error);
            }
            foreach (object item in clbPermisos.Items)
                item.GetType().GetProperty("IsSelected").SetValue(item, false, null);
            cbGrupos.SelectedIndex = -1;
            cbUsuarios.SelectedIndex = -1;
            _busyIndicator.IsBusy = false;
            originalGrupo = null;
        }

        private void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            permisos = roleManager.GetAllPermisos();
            permisos.Sort();
            grupos = roleManager.GetNombresGrupos();
            grupos.Sort();
        }

        private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            Xceed.Wpf.Toolkit.Primitives.SelectorItem item;
            List<string> aux;

            if (permisos != null)
            {
                foreach (string permiso in permisos)
                {
                    item = new Xceed.Wpf.Toolkit.Primitives.SelectorItem();
                    item.Content = permiso;
                    clbPermisos.Items.Add(item);
                }
            }

            if (grupos != null)
            {
                aux = new List<string>();
                aux.Add("-- Sin grupo --");
                aux.AddRange(grupos);
                cbGrupos.ItemsSource = aux;
                cbGrupos.SelectedIndex = -1;
            }
            _busyIndicator.IsBusy = false;
        }

        private void workerChangeUsuario_DoWork(object sender, DoWorkEventArgs e)
        {
            originalGrupo = null;
            grupoUsuario = roleManager.GetGrupoUsuario(usuario);
        }

        private void workerChangeUsuario_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            string objeto;
            List<string> originalPerm;

            if (grupoUsuario != null)
            {
                cbGrupos.IsEnabled = clbPermisos.IsEnabled = btAceptar.IsEnabled = true;
                cbGrupos.SelectedIndex = -1;
                // Una vez tenemos el grupo, lo seleccionamos en el ComboBox y marcamos los permisos que tiene
                for (int i = cbGrupos.Items.Count - 1; i >= 0; i--)
                {
                    objeto = cbGrupos.Items[i].ToString();
                    if (objeto.Equals(grupoUsuario.Nombre))
                    {
                        chargingGroup = true;
                        cbGrupos.SelectedIndex = i;
                        break;
                    }
                }

                // Marcamos los permisos seleccionados para el grupo al que pertenece
                // Hay que marcar/desmarcar los casos especiales de este usuario
                foreach (object item in clbPermisos.Items)
                {
                    objeto = item.GetType().GetProperty("Content").GetValue(item, null).ToString();
                    if (grupoUsuario.Permisos.Contains(objeto))
                        item.GetType().GetProperty("IsSelected").SetValue(item, true, null);
                    if (grupoUsuario.PermisosBorrados.Contains(objeto))
                        item.GetType().GetProperty("IsSelected").SetValue(item, false, null);
                    if (grupoUsuario.PermisosNuevos.Contains(objeto))
                        item.GetType().GetProperty("IsSelected").SetValue(item, true, null);
                }

                originalPerm = new List<string>();
                originalPerm.AddRange(grupoUsuario.Permisos);
                originalPerm = originalPerm.Except(grupoUsuario.PermisosBorrados).ToList();
                originalPerm.AddRange(grupoUsuario.PermisosNuevos);
                originalGrupo = new GrupoValue(grupoUsuario.Nombre, originalPerm.Distinct().ToArray());
            }
            else
                cbGrupos.IsEnabled = clbPermisos.IsEnabled = btAceptar.IsEnabled = false;
            _busyIndicator.IsBusy = false;
        }

        private void workerChangeGrupo_DoWork(object sender, DoWorkEventArgs e)
        {
            grupoUsuario = roleManager.GetGrupo(grupo);
        }

        private void workerChangeGrupo_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            object objeto;

            if (grupoUsuario != null)
            {
                foreach (object item in clbPermisos.Items)
                {
                    objeto = item.GetType().GetProperty("Content").GetValue(item, null).ToString();
                    if (grupoUsuario.Permisos.Contains(objeto))
                        item.GetType().GetProperty("IsSelected").SetValue(item, true, null);
                }
            }
        }

        private void cbUsuarios_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (cbUsuarios.SelectedIndex != -1)
            {
                if (HasChanges())
                {
                    if (Xceed.Wpf.Toolkit.MessageBox.Show("Si cambia de usuario los cambios se perderán, ¿desea seguir?", "Confirmar",
                        MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.No)
                        return;
                }

                _busyIndicator.IsBusy = true;
                if (!workerChangeUsuario.IsBusy)
                {
                    // Marcar todas las casillas como deseleccionadas
                    foreach (object item in clbPermisos.Items)
                        item.GetType().GetProperty("IsSelected").SetValue(item, false, null);

                    usuario = cbUsuarios.SelectedValue.ToString();
                    workerChangeUsuario.RunWorkerAsync();
                }
            }
            else
                cbGrupos.IsEnabled = clbPermisos.IsEnabled = btAceptar.IsEnabled = false;
        }

        private void btCancelar_Click(object sender, RoutedEventArgs e)
        {
            Close();
        }

        private void Close()
        {
            ((DocumentContent)this.Parent).Close();
        }

        private void ClosingDocument(object sender, CancelEventArgs e)
        {
            if (!success)
            {
                if (HasChanges())
                {
                    if (Xceed.Wpf.Toolkit.MessageBox.Show("¿Desea salir sin aplicar los cambios?", "Salir",
                        MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.No)
                        e.Cancel = true;
                }
            }
        }

        public void AddEventHandler()
        {
            ((DocumentContent)this.Parent).Closing += ClosingDocument;
        }

        private void cbGrupos_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (cbGrupos.SelectedIndex != -1 && !chargingGroup)
            {
                if (!workerChangeGrupo.IsBusy)
                {
                    // Limpiar los permisos de ese grupo y coger permisos nuevos
                    foreach (object item in clbPermisos.Items)
                        item.GetType().GetProperty("IsSelected").SetValue(item, false, null);

                    grupo = cbGrupos.SelectedValue.ToString();
                    workerChangeGrupo.RunWorkerAsync();
                }

            }
            chargingGroup = false;
        }

        private bool HasChanges()
        {
            List<string> permList;
            string[] perm;
            bool permisosDistintos;

            if (originalGrupo != null)
            {
                permList = new List<string>();
                foreach (object item in clbPermisos.Items)
                {
                    if (Convert.ToBoolean(item.GetType().GetProperty("IsSelected").GetValue(item, null)))
                        permList.Add(item.GetType().GetProperty("Content").GetValue(item, null).ToString());
                }
                perm = permList.ToArray();

                permisosDistintos = perm.Except(originalGrupo.Permisos).Count() != 0 ||
                    originalGrupo.Permisos.Except(perm).Count() != 0;

                if ((cbGrupos.SelectedIndex != -1 && !cbGrupos.SelectedValue.ToString().Equals(originalGrupo.Nombre))
                    || permisosDistintos)
                    return true;
            }
            return false;
        }

        private void btAceptar_Click(object sender, RoutedEventArgs e)
        {
            List<string> aux;

            if (HasChanges())
            {
                if (!workerCambios.IsBusy)
                {
                    grupoSeleccionado = cbGrupos.SelectedValue.ToString();
                    userSeleccionado = cbUsuarios.SelectedValue.ToString();
                    // Coger los permisos marcados
                    aux = new List<string>();
                    foreach (object item in clbPermisos.Items)
                    {
                        if (Convert.ToBoolean(item.GetType().GetProperty("IsSelected").GetValue(item, null)))
                            aux.Add(item.GetType().GetProperty("Content").GetValue(item, null).ToString());
                    }
                    permisosMarcados = aux.ToArray();
                    _busyIndicator.IsBusy = true;
                    workerCambios.RunWorkerAsync();
                }
            }
            else
            {
                Xceed.Wpf.Toolkit.MessageBox.Show("No hay cambios realizados sobre este usuario", "Error",
                    MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }
        }
    }
}
